AWS SDK for Java v1でSIGV4の署名を作成してIAM認証付きAPI Gatewayのエンドポイントにリクエストする
CX事業本部@大阪の岩田です。
現在関わっている案件でIAM認証を付けたAPI GWのエンドポイントにリクエストを発行する必要が出てきました。Node.jsに関しては先日佐藤が調べた通りなのですが、私の案件では諸々の理由からAWS SDK for Javaのv1を使う必要があったのでやり方を調べてみました。
Node.jsの場合はこちら↓
要件的には
- 現在AWS SDK for Javaのv1を使ったJavaのプログラムが稼働しており、AWS SDK for Javaのv2には上げたくない
- API GWのSDK生成機能は使いたくない
- SIGV4の署名ロジックを独自実装するのではなく、実績のあるライブラリに乗っかりたい → 可能ならAWS SDK for Java v1でやりたい。
といった要件になります。わざわざSIGv4の署名を作らなくてもAPI GatewayのAPIキーで認証してしまえば楽なのですが、APIキーを認証目的で利用することは非推奨なので、頑張ってSIGv4の署名を作成します。
認証および API の認証の唯一の手段として API キーに依存しないようにしてください。まず、使用量プランに複数の API がある場合、その使用量プランの 1 つの API に対して有効な API キーを持つユーザーは、その使用量プランのすべての API にアクセスできます。代わりに、IAM ロール、Lambda オーソライザー、または Amazon Cognito ユーザープールを使用します。
API キーを使用する使用量プランの作成と使用
環境
- Java SE 1.8.0_171
- aws-java-sdk-core-1.11.727
やり方
com.amazonaws.auth.AWS4Signer
クラスのsign
メソッドを使うことでSIGv4の署名を作成できます。sign
メソッドの引数にはインターフェースSignableRequest
を渡してやる必要があります。汎用的な具象クラスとしてcom.amazonaws.DefaultRequest
が使えるので、こいつを使いましょう。
重要なのは以下のポイントです。
DefaultRequest
のコンストラクタにexecute-api
を渡してやるDefaultRequest
のsetEndpoint
でAPI Gatewayのエンドポイントを設定するDefaultRequest
のsetResourcePath
で実行したいAPIのリソースパスを設定するDefaultRequest
のsetHttpMethod
で実行したいAPIのHTTPメソッドを指定する
package com.classmethod.awssdkexample; import com.amazonaws.auth.AWS4Signer; import com.amazonaws.auth.BasicSessionCredentials; import com.amazonaws.AmazonWebServiceResponse; import com.amazonaws.ClientConfiguration; import com.amazonaws.DefaultRequest; import com.amazonaws.http.AmazonHttpClient; import com.amazonaws.http.HttpMethodName; import com.amazonaws.Response; import java.util.Date; import java.net.URI; public class Main { public static void main(String[] args) { AWS4Signer signer = new AWS4Signer(); String awsAccessKey = "<アクセスキー>"; String awsSecretKey = "<シークレットキー>"; String sessionToken = "<セッショントークン>"; BasicSessionCredentials cred = new BasicSessionCredentials( awsAccessKey, awsSecretKey, sessionToken ); DefaultRequest req = new DefaultRequest("execute-api"); req.setEndpoint(URI.create("https://<API GWのエンドポイント>.execute-api.ap-northeast-1.amazonaws.com")); req.setResourcePath("<実行したいAPIのパス 例:/pets>"); req.setHttpMethod(HttpMethodName.fromValue("<実行したいAPIのHTTPメソッド 例:GET>")); signer.setOverrideDate(new Date()); signer.setRegionName("ap-northeast-1"); signer.setServiceName("execute-api"); signer.sign(req, cred); ClientConfiguration clientConfiguration = new ClientConfiguration(); AmazonHttpClient client = new AmazonHttpClient(clientConfiguration); Response<AmazonWebServiceResponse<String>> res = client.requestExecutionBuilder() .request(req) .execute(new StringResponseHandler()); System.out.println(res.getAwsResponse().getResult()); } }
今回はAPIのレスポンスをそのまま標準出力に吐き出したいだけだったので、Stack Overflowの記事を参考にHttpResponseHandler
インターフェースを実装したクラスを作成してAPI呼び出し時のハンドラーに指定しています。今回の検証はSIGv4の署名を作成してIAM認証を通すまでがゴールなので、本来考慮すべき諸々の処理を省略しています。もし実際に本番利用される場合は適宜処理を修正/追加頂くようお願いします。
package com.classmethod.awssdkexample; import com.amazonaws.AmazonWebServiceResponse; import com.amazonaws.http.HttpResponseHandler; import com.amazonaws.util.IOUtils; import java.io.IOException; public class StringResponseHandler implements HttpResponseHandler<AmazonWebServiceResponse<String>> { @Override public AmazonWebServiceResponse<String> handle(com.amazonaws.http.HttpResponse response) throws IOException { AmazonWebServiceResponse<String> awsResponse = new AmazonWebServiceResponse<>(); awsResponse.setResult(IOUtils.toString(response.getContent())); return awsResponse; } @Override public boolean needsConnectionLeftOpen() { return false; } }
まとめ
Javaの経験が無いので大変でしたが、ドキュメントを見つつなんとか実装できて良かったです。